package gat1400

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"time"

	"gitee.com/general252/gat1400/util"
	"gitee.com/general252/gat1400/viid"
	"github.com/gin-gonic/gin"

	auth "github.com/abbot/go-http-auth"
)

type Server struct {
	devices *util.Map[string, *Device]
}

func NewServer() *Server {
	return &Server{
		devices: util.NewMap[string, *Device](),
	}
}

func (tis *Server) Serve() {
	r := gin.Default()

	authenticator := auth.NewDigestAuthenticator("gat1400.com", tis._secret)
	r.POST(viid.SecurityURLVIIDRegister, tis._digestAuthWrap(authenticator, tis.register))     // 注册
	r.POST(viid.SecurityURLVIIDUnRegister, tis._digestAuthWrap(authenticator, tis.unRegister)) // 注销
	r.POST(viid.SecurityURLVIIDKeepalive, tis.keepalive)                                       // 保活
	r.GET(viid.SecurityURLVIIDTime, tis.time)                                                  // 校时

	r.POST(viid.SecurityURLVIIDFaces, tis.facesPost)                 // 人脸批量增加 (TODO: 通知订阅的上级平台, 记录到数据数据库中, 记录每一条和订阅的状态, 保障通知到上级平台)
	r.POST(viid.SecurityURLVIIDMotorVehicles, tis.motorVehiclesPost) // 批量机动车增加 (TODO: 通知订阅的上级平台)

	r.POST(viid.SecurityURLVIIDSubscribes, tis.subscribes)                         // 批量订阅(作为下级平台, 被上级平台订阅)
	r.POST(viid.SecurityURLVIIDSubscribeNotifications, tis.subscribeNotifications) // 订阅通知(作为上级平台, 接收下级平台的通知)

	_ = r.Run(":11400")
}

// @summary 注册
// @Accept  json
// @Produce json
// @Param data   body      viid.RegisterObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusObject	"ok"
// @Router /VIID/System/Register [post]
func (tis *Server) register(c *gin.Context, username string) {
	var (
		request    viid.RegisterObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusObject{
			ResponseStatusObject: viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOtherError,
				StatusString: "",
				LocalTime:    localTime,
			},
		}
		httpCode = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	if err := c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusObject.StatusCode = viid.StatusCodeInvalidJSONContent
		reply.ResponseStatusObject.StatusString = err.Error()
		return
	}

	// 设备上线
	{
		deviceId := string(request.RegisterObject.DeviceID)
		objectDevice, ok := tis.devices.Load(deviceId)
		if !ok {
			objectDevice = NewDevice(deviceId)
			tis.devices.Store(deviceId, objectDevice)
		}

		objectDevice.SetOnline(true)
	}

	httpCode = http.StatusOK
	reply.ResponseStatusObject = viid.ResponseStatus{
		Id:           string(request.RegisterObject.DeviceID),
		RequestURL:   requestURL,
		StatusCode:   viid.StatusCodeOK,
		StatusString: "OK",
		LocalTime:    localTime,
	}
}

// @summary 注销
// @Accept  json
// @Produce json
// @Param data   body      viid.UnRegisterObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusObject	"ok"
// @Router /VIID/System/UnRegister [post]
func (tis *Server) unRegister(c *gin.Context, username string) {
	var (
		request    viid.UnRegisterObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusObject{
			ResponseStatusObject: viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOtherError,
				StatusString: "",
				LocalTime:    localTime,
			},
		}
		httpCode = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	if err := c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusObject.StatusCode = viid.StatusCodeInvalidJSONContent
		reply.ResponseStatusObject.StatusString = err.Error()
		return
	}

	// 设备下线
	{
		deviceId := string(request.UnRegisterObject.DeviceID)
		objectDevice, ok := tis.devices.Load(deviceId)
		if !ok {
			httpCode = http.StatusNotFound
			reply.ResponseStatusObject.StatusCode = viid.StatusCodeInvalidOperation
			reply.ResponseStatusObject.StatusString = "not found device"
			return
		}

		objectDevice.SetOnline(false)
	}

	httpCode = http.StatusOK
	reply.ResponseStatusObject = viid.ResponseStatus{
		Id:           string(request.UnRegisterObject.DeviceID),
		RequestURL:   requestURL,
		StatusCode:   viid.StatusCodeOK,
		StatusString: "OK",
		LocalTime:    localTime,
	}
}

// @summary 保活
// @Accept  json
// @Produce json
// @Param data   body      viid.KeepaliveObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusObject	"ok"
// @Router /VIID/System/Keepalive [post]
func (tis *Server) keepalive(c *gin.Context) {
	var (
		request    viid.KeepaliveObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusObject{
			ResponseStatusObject: viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOtherError,
				StatusString: "",
				LocalTime:    localTime,
			},
		}
		httpCode = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	// 登录标识
	objectDevice, err := tis._getUserIdentify(c)
	if err != nil {
		httpCode = http.StatusBadRequest
		reply.SetResponse(viid.StatusCodeDeviceError, err.Error())
		return
	}

	// 获取参数
	if err = c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusObject.StatusCode = viid.StatusCodeInvalidJSONContent
		reply.ResponseStatusObject.StatusString = err.Error()
		return
	}

	// 检查
	if string(request.KeepaliveObject.DeviceID) != objectDevice.ID() {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusObject.StatusCode = viid.StatusCodeDeviceError
		reply.ResponseStatusObject.StatusString =
			fmt.Sprintf("KeepaliveObject.DeviceID(%v) <> User-Identify(%v)",
				request.KeepaliveObject.DeviceID, objectDevice.ID())
		return
	}

	// 更新时间
	objectDevice.OnKeepalive()

	// 回复
	httpCode = http.StatusOK
	reply.ResponseStatusObject = viid.ResponseStatus{
		Id:           string(request.KeepaliveObject.DeviceID),
		RequestURL:   requestURL,
		StatusCode:   viid.StatusCodeOK,
		StatusString: "OK",
		LocalTime:    localTime,
	}
}

// @summary 校时
// @Accept  json
// @Produce json
// @Success 200  {object}  viid.SystemTimeObject	"ok"
// @Router /VIID/System/Keepalive [get]
func (tis *Server) time(c *gin.Context) {
	c.JSON(http.StatusOK, viid.SystemTimeObject{
		SystemTimeObject: viid.SystemTime{
			VIIDServerID: "1",
			TimeMode:     "1",
			LocalTime:    viid.NewDateTime(time.Now()),
			TimeZone:     "Asia/Shanghai",
		},
	})
}

// @summary 人脸批量增加
// @Accept  json
// @Produce json
// @Param data   body      viid.FaceListObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusListObject	"ok"
// @Router /VIID/Faces [post]
func (tis *Server) facesPost(c *gin.Context) {
	var (
		request    viid.FaceListObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusListObject{}
		httpCode   = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	// 登录标识
	objectDevice, err := tis._getUserIdentify(c)
	if err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeDeviceError,
				StatusString: err.Error(),
				LocalTime:    localTime,
			})
		return
	}
	_ = objectDevice

	if err := c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeInvalidOperation,
				StatusString: err.Error(),
				LocalTime:    localTime,
			},
		)
		return
	}

	for _, face := range request.FaceListObject.FaceObject {
		_ = face.FaceID
		_ = face.SubImageList

		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           face.FaceID.String(),
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOK,
				StatusString: "OK",
				LocalTime:    localTime,
			},
		)
	}

	httpCode = http.StatusOK
}

// @summary 批量机动车增加
// @Accept  json
// @Produce json
// @Param data   body      viid.MotorVehicleListObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusListObject	"ok"
// @Router /VIID/MotorVehicles [post]
func (tis *Server) motorVehiclesPost(c *gin.Context) {
	var (
		request    viid.MotorVehicleListObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusListObject{}
		httpCode   = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	// 登录标识
	objectDevice, err := tis._getUserIdentify(c)
	if err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeDeviceError,
				StatusString: err.Error(),
				LocalTime:    localTime,
			})
		return
	}
	_ = objectDevice

	if err := c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeInvalidOperation,
				StatusString: err.Error(),
				LocalTime:    localTime,
			},
		)
		return
	}

	for _, face := range request.MotorVehicleListObject.MotorVehicleObject {
		_ = face.MotorVehicleID
		_ = face.SubImageList

		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           face.MotorVehicleID.String(),
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOK,
				StatusString: "OK",
				LocalTime:    localTime,
			},
		)
	}

	httpCode = http.StatusOK
}

// @summary 非批量机动车增加
// @Accept  json
// @Produce json
// @Param data   body      viid.NonMotorVehicleListObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusListObject	"ok"
// @Router /VIID/MotorVehicles [post]
func (tis *Server) nonMotorVehiclesPost(c *gin.Context) {
	var (
		request    viid.NonMotorVehicleListObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusListObject{}
		httpCode   = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	// 登录标识
	objectDevice, err := tis._getUserIdentify(c)
	if err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeDeviceError,
				StatusString: err.Error(),
				LocalTime:    localTime,
			})
		return
	}
	_ = objectDevice

	if err := c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeInvalidOperation,
				StatusString: err.Error(),
				LocalTime:    localTime,
			},
		)
		return
	}

	for _, face := range request.NonMotorVehicleListObject.NonMotorVehicleObject {
		_ = face.NonMotorVehicleID
		_ = face.SubImageList

		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           face.NonMotorVehicleID.String(),
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOK,
				StatusString: "OK",
				LocalTime:    localTime,
			},
		)
	}

	httpCode = http.StatusOK
}

// @summary 批量订阅
// @Accept  json
// @Produce json
// @Param data   body      viid.SubscribeListObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusListObject	"ok"
// @Router /VIID/Subscribes [post]
func (tis *Server) subscribes(c *gin.Context) {
	var (
		request    viid.SubscribeListObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusListObject{}
		httpCode   = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	// 登录标识
	objectDevice, err := tis._getUserIdentify(c)
	if err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeDeviceError,
				StatusString: err.Error(),
				LocalTime:    localTime,
			})
		return
	}
	_ = objectDevice

	if err := c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeDeviceError,
				StatusString: err.Error(),
				LocalTime:    localTime,
			},
		)
		return
	}

	for _, subscribe := range request.SubscribeListObject.SubscribeObject {
		log.Println("接收订阅的地址: ", subscribe.ReceiveAddr)
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           subscribe.SubscribeID,
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOK,
				StatusString: "",
				LocalTime:    localTime,
			})
	}

	httpCode = http.StatusOK
}

// @summary 订阅通知
// @Accept  json
// @Produce json
// @Param data   body      viid.SubscribeNotificationListObject       true  "data"
// @Success 200  {object}  viid.ResponseStatusListObject	"ok"
// @Router /VIID/SubscribeNotifications [post]
func (tis *Server) subscribeNotifications(c *gin.Context) {
	var (
		request    viid.SubscribeNotificationListObject
		requestURL = c.Request.URL.String()
		localTime  = viid.NewDateTime(time.Now())
		reply      = &viid.ResponseStatusListObject{}
		httpCode   = http.StatusNotImplemented
	)

	defer func() {
		c.JSON(httpCode, reply)
	}()

	// 登录标识
	objectDevice, err := tis._getUserIdentify(c)
	if err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeDeviceError,
				StatusString: err.Error(),
				LocalTime:    localTime,
			})
		return
	}
	_ = objectDevice

	if err := c.ShouldBindJSON(&request); err != nil {
		httpCode = http.StatusBadRequest
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           "",
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeDeviceError,
				StatusString: err.Error(),
				LocalTime:    localTime,
			},
		)
		return
	}

	for _, subscribe := range request.SubscribeNotificationListObject.SubscribeNotificationObject {
		reply.ResponseStatusList.ResponseStatusObject = append(reply.ResponseStatusList.ResponseStatusObject,
			viid.ResponseStatus{
				Id:           subscribe.SubscribeID,
				RequestURL:   requestURL,
				StatusCode:   viid.StatusCodeOK,
				StatusString: "",
				LocalTime:    localTime,
			})
	}

	httpCode = http.StatusOK
}

// ---------------------------------------------------------------------------------------------------------------------

// 摘要认证
func (tis *Server) _digestAuthWrap(authenticator *auth.DigestAuth, wrapped func(c *gin.Context, username string)) gin.HandlerFunc {
	return func(c *gin.Context) {
		var (
			w = c.Writer
			r = c.Request
			a = authenticator
		)

		if username, authInfo := a.CheckAuth(r); username == "" {
			a.RequireAuth(w, r)
		} else {
			if authInfo != nil {
				w.Header().Set(a.Headers.V().AuthInfo, *authInfo)
			}

			wrapped(c, username)
		}
	}
}

// 验证用户密码
func (tis *Server) _secret(user, realm string) string {
	var uses = map[string]string{
		"34078100001190001003": "123456",
	}

	log.Printf("secret %v %v", user, realm)

	password, ok := uses[user]
	if !ok {
		return ""
	}

	h := md5.New()
	_, _ = h.Write([]byte(fmt.Sprintf("%v:%v:%v", user, realm, password)))
	hashValue := h.Sum(nil)

	return hex.EncodeToString(hashValue)
}

// 登录标识
func (tis *Server) _getUserIdentify(c *gin.Context) (*Device, error) {
	deviceId := c.GetHeader(viid.UserIdentify)
	if len(deviceId) == 0 {
		return nil, fmt.Errorf("no User-Identify value")
	}

	objectDevice, ok := tis.devices.Load(deviceId)
	if !ok {
		return nil, fmt.Errorf("not found")
	}

	if !objectDevice.IsOnline() {
		return objectDevice, fmt.Errorf("device not online")
	}

	return objectDevice, nil
}
